#include "Hopfield.h"

using namespace std;

Hopfield::Hopfield(){loadConfig("default.txt");}

Hopfield::Hopfield(Hopfield* H){
	this->W = H->W;
	this->N = H->N;
}

Hopfield::Hopfield(string file){
	loadConfig(file);
}



/**
	Cargo la configuracion desde el txt
**/
void Hopfield::loadConfig(string file){
	ifstream archi;
	archi.open(file.c_str());

	if(archi.is_open()){
		string str_line;
		char* line;
		//Titulo
		getline(archi,str_line);
		//Número de neuronas
		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		this->N = atoi(line);
		//Máximo de Iteraciones
		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		this->maxIt = atoi(line);
		//Iteraciones estables
		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		this->numEstables = atoi(line);
		}		
	archi.close();

	this->W.resize(this->N);
	this->y.resize(this->N);
	for(int i=0;i<this->N;i++){
		this->W[i].resize(this->N); //aca hay un peso demas, pero dejo asi por simplicidad	
	}
}

/**
	Entrenamiento Hessiano, NO SUPERVISADO, NO ITERATIVO
**/
void Hopfield::training(vector<vector<int> >& X){
	int nData = X.size();

	for(int i=0;i<this->N;i++){
		for(int j=i;j<this->N;j++){ // "j" empieza desde "i" porque es W simetrico (Wij = Wji)
			if(i!=j){
				double aux = 0;
				for(int k=0;k<nData;k++){
					aux += X[k][i]*X[k][j];	
				}			
				aux /= (double)nData;
				this->W[i][j] = aux;
				this->W[j][i] = aux;
			}
			else
				this->W[i][i] = 0; 	// lo pongo a 0 para representar que no existe esta conexion (Wii=0), y poder calcula el producto punto mas abajo
		}
		
		/*cout<<"W["<<i<<"]: "<<endl;
		view_vd(W[i]);
		cin.get();*/
	}
	cout<<"Entrenamiento realizado con Exito"<<endl;
}


/**
	Recuperación del patrón original, ITERATIVO, RECURSIVO
	Busca encontrar un mínimo en la función de Energía

**/
vector<int> Hopfield::output(vector<int> &x){
	//srand(time(NULL));
	this->y = x;
	vector<int> yAnt = y;  // y(n-1)
	bool next = true;
	int it = 1;
	vector<int> indRandomize;	//vector para acceso "NO secuencial"
  	indRandomize.resize(this->N);
 	for(int i=0;i<this->N;i++) indRandomize[i] = i;
	int num_estables = 0; //numero de vueltas estables;
	while(next){
		bool estable = true;
		for(int i=0;i<this->N;i++){
			//random_shuffle(indRandomize.begin(),indRandomize.end()); //Aleatorizo los indices
			int j = rand()%this->N;
			//int j = indRandomize[i];
			//int j = i;
			double aux = prod_punto(W[j],y);
			if(aux != 0.0)	// si <Wj,y> = 0, mantengo yj(n) = yj(n-1)				
				this->y[j] = sign(aux);
			//if(this->y[j] != yAnt[j]){
			//	estable = false; // si hubo algun cambio ya no es estable la salida
			//}
		}

		//view_vi(this->y);
	
		for(int i=0;i<this->N;i++){
			if(this->y[i] != yAnt[i]){
				estable = false;
				break;
			}
		} 
		if(it >= this->maxIt){
			cout<<"Salida por iteraciones: "<<it<<endl;
			next = false;
		}
		/*if(estable){
			cout<<"Salida por estable: "<<it<<endl;
			next = false;
			view_vi(this->y);
		}*/
		
		if(estable)
			num_estables ++;
		else
			num_estables = 0;
		if(num_estables > this->numEstables){
			cout<<"Salida por Secuencia estable: "<<it<<endl;
			next = false;
			return this->y;
		}
		it ++;
		yAnt = this->y;
	}
	
	//for(int i=0;i<this->N;i++)
	//	this->y[i] = 1; //retorno todos unos como referencia a que no pudo solucionar el problema
	return this->y;

}
